import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';

import 'package:crypto/crypto.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_sound/flutter_sound.dart';
import 'package:flutter_sound/public/tau.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:uuid/uuid.dart';
import 'package:web_socket_channel/io.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:yd_eval/entity/yd_api_eval_result_entity.dart';

import '../entity/yd_eval_entity.dart';
import '../entity/yd_eval_result_entity.dart';

class YdEvalType {
  static const String TYPE_ZH = "zh-CHS";
  static const String TYPE_EN = "en";

  static const String HOST = "openapi.youdao.com";

  /// 流式
  static const String REAL_TIME_URL = "wss://$HOST/stream_capt?";
  static const String APP_KEY = "4a1f5b821f776346";
  static const String APP_SECRET = "KqOPbZHLOSD7J1U872WMW7Jy3v9RK3Pu";
}

typedef OnResultListener = void Function(YDEvalResultEntity result);

/// 有道评测管理器
class YdEvalManager {
  static final YdEvalManager _instance = YdEvalManager._internal();

  static YdEvalManager get instance => _instance;

  YdEvalManager._internal() {}

  /// socket客户端
  WebSocketChannel? _socketChannel;

  /// 录音
  FlutterSoundRecorder recorder = FlutterSoundRecorder();

  /// 播放器
  FlutterSoundPlayer player = FlutterSoundPlayer();

  /// 录音数据
  StreamSubscription? _mRecordingDataSubscription;

  OnResultListener? _listener;

  bool _isConvertWav = false;

  /// 是否取消评测 （就是不触发完成回调）
  bool _isCancel = false;

  /// 录音文件路径
  String _mPath = '';

  /// wav 录音文件路径
  String _wavPath = '';

  /// 评测原文
  String _originalText = '';

  /// 采样率
  final int tSampleRate = 16000;

  StreamSubscription? _playListener;

  /// 播放录音回调
  Function(PlaybackDisposition playback)? _onPlayback;

  String evalType = YdEvalType.TYPE_EN;

  void init(String appKey, String appSecret) {}

  void setResultListener(OnResultListener listener) {
    _listener = listener;
  }

  void startEval(YdEvalEntity entity) {
    _playListener?.cancel();
    _connect(entity);
  }

  String getUrl(String text, String langType) {
    var salt = const Uuid().v1();
    var curTime = DateTime.now().millisecondsSinceEpoch ~/ 1000;
    var signStr = "${YdEvalType.APP_KEY}$salt$curTime${YdEvalType.APP_SECRET}";
    var sign = getDigest(signStr);
    var format = "wav";
    var vadHeadSil = "3000"; // 前置超时
    var vadTailSil = "2000"; // 后置超时
    var url = "${YdEvalType.REAL_TIME_URL}appKey=${YdEvalType.APP_KEY}&"
        "salt=$salt&curtime=$curTime&sign=$sign&signType=v4&"
        "langType=$langType&format=$format&channel=1&"
        "version=v1&rate=16000&vadHeadSil=$vadHeadSil&vadTailSil=$vadTailSil";
    debugPrint("有道链接： $url , curTime => $curTime ,salt => $salt");
    return url;
  }

  String getDigest(String? input) {
    if (input == null) {
      return "";
    }

    // 将字符串转换为 UTF-8 字节
    var bytes = const Utf8Encoder().convert(input);

    // 计算 SHA-256 哈希
    var digest = sha256.convert(bytes);

    // 返回十六进制字符串
    return digest.toString().toUpperCase();
  }

  void _connect(YdEvalEntity entity) async {
    if (!await Permission.microphone.request().isGranted) {
      _listener?.call(
        YDEvalResultEntity(
            path: "",
            result: "",
            type: YDEvalEventType.toast,
            toastText: "没有录音权限!"),
      );
      return;
    }
    try {
      if (_socketChannel == null) {
        _socketChannel = IOWebSocketChannel.connect(
            // Uri.parse(getUrl(entity.txt, entity.langType)),
            getUrl(entity.txt, entity.langType),
            headers: {
              "Host": YdEvalType.HOST,
            },
            connectTimeout: const Duration(seconds: 20));
        _openRecord();
        evalType = entity.langType;
        await _socketChannel!.ready;
        _isConvertWav = false;
        _socketChannel!.stream.listen((message) {
          debugPrint('返回数据:$message');
          handleMessage(message);
        }, onError: (error) async {
          debugPrint('socket 连接出错！');
          _listener?.call(YDEvalResultEntity(
              path: _mPath,
              result: "",
              type: YDEvalEventType.error,
              originText: _originalText));
          stopRecord();
          closeSocket();
        }, onDone: () {
          debugPrint('socket 连接关闭');
          stopRecord();
        });
      }
      _originalText = entity.txt;

      // _socketChannel!.sink.add(_getFirstData(entity));
    } catch (err) {
      debugPrint('WebSocketChannel err:$err');
      var code = 1001;
      if (err is TimeoutException) {
        // ToastUtils.show('当前网络不佳，请保持网络通畅后重试');
        code = 1002;
      }
      closeSocket();
      _listener?.call(YDEvalResultEntity(
          path: "",
          result: "",
          type: YDEvalEventType.error,
          originText: _originalText,
          errorCode: code));
    }
  }

  void closeSocket() {
    _socketChannel?.sink.close();
    _socketChannel = null;
  }

  void handleMessage(String message) {
    try {
      final messageObj = jsonDecode(message); // 将 JSON 字符串解析为 Map
      final action = messageObj['action'];

      switch (action) {
        case 'started':
          startRecord(); // 开始录音
          String content = _originalText.replaceAll('"', ' '); // 替换内容中的双引号
          String data = jsonEncode({'text': content}); // 创建新的 JSON 字符串
          if (_socketChannel != null) {
            debugPrint("onMessage 发送数据 ：$data"); // 日志输出
            try {
              _socketChannel!.sink.add(data); // 发送数据
            } catch (e) {
              debugPrint("started发送数据！！ 异常: ${e.toString()}");
            }
          }
          break;

        case 'capt':
          final result = messageObj['result'];
          final intonation = result['intonation'];
          if (intonation != null) {
            final isFinal = result['isFinal'];
            if (isFinal) {
              YdApiEvalResultEntity entity =
                  YdApiEvalResultEntity.fromJson(messageObj);
              Timer.periodic(const Duration(milliseconds: 100), (timer) {
                debugPrint('正在等待pcm转换为wav $_isConvertWav');
                if (_isConvertWav) {
                  timer.cancel();
                  _isConvertWav = false;

                  /// 将数据抛出给业务处理
                  _listener?.call(YDEvalResultEntity(
                    path: _wavPath,
                    result: message,
                    resultEntity: entity,
                    type: YDEvalEventType.evalResult,
                    originText: _originalText,
                    evalType: evalType,
                  )); // 发送结果
                }
              });
            } else {
              _listener?.call(YDEvalResultEntity(
                path: _mPath,
                result: message,
                type: YDEvalEventType.evalProgress,
                originText: _originalText,
                evalType: evalType,
              )); // 发送评估进度
            }
          }
          break;

        case 'error':
          stopRecord(); // 停止录音
          closeSocket();
          _listener?.call(YDEvalResultEntity(
            path: _mPath,
            result: message,
            type: YDEvalEventType.error,
            evalType: evalType,
          )); // 发送错误
          break;

        case 'close':
          stopRecord(); // 停止录音
          _listener?.call(YDEvalResultEntity(
            path: _mPath,
            result: message,
            type: YDEvalEventType.close,
            evalType: evalType,
          )); // 发送评估关闭
          closeSocket(); // 关闭 Socket
          break;

        default:
          break;
      }
    } catch (e) {
      print("评测异常：${e.toString()}");
      _listener?.call(YDEvalResultEntity(
        path: _mPath,
        result: message,
        type: YDEvalEventType.error,
        evalType: evalType,
      )); // 发送错误
    }
  }

  /// 创建录音文件
  Future<IOSink> createFile() async {
    // var tempDir = await getTemporaryDirectory();
    var tempDir = await getTemporaryDirectory();
    _mPath = '${tempDir.path}/eval_audio.pcm';
    _wavPath = '${tempDir.path}/eval_audio.wav';
    var outputFile = File(_mPath);
    if (outputFile.existsSync()) {
      await outputFile.delete();
    }
    debugPrint('_mPath ==> $_mPath');
    return outputFile.openWrite();
  }

  /// 开启录音 在真正录音前需要先开启录音
  void _openRecord() async {
    await recorder.openRecorder();

    // startRecord();
  }

  /// 开始录音
  void startRecord() async {
    _listener?.call(YDEvalResultEntity(
      path: _mPath,
      result: "",
      type: YDEvalEventType.startRecord,
      originText: _originalText,
      evalType: evalType,
    ));
    var sink = await createFile();
    var recordingDataController = StreamController<Uint8List>();
    _mRecordingDataSubscription =
        recordingDataController.stream.listen((buffer) {
      sink.add(buffer);
      // 将音频数据分割为固定大小的帧
      int frameSize = 1280; // 每帧音频数据大小为 1280 字节
      int offset = 0;
      while (offset < buffer.length) {
        int end = offset + frameSize;
        if (end > buffer.length) {
          end = buffer.length;
        }
        Uint8List frame = buffer.sublist(offset, end);
        // debugPrint('实时流：$frame');
        // String audioBase64String = base64Encode(Uint8List.fromList(frame));

        /// 发送实时流数据到有道
        _socketChannel?.sink.add(frame);

        offset += frameSize;
      }
    });

    /// 必须要调用这个 否则 onProgress 不回调
    // recorder.setSubscriptionDuration(const Duration(milliseconds: 100));

    // _dbPeakSubscription = recorder.onProgress?.listen((event) {
    //   debugPrint('onProgress : ${event.duration}, event.decibels -> ${event.decibels}');
    // });

    /// 开始录音
    await recorder.startRecorder(
      toStream: recordingDataController.sink,
      codec: Codec.pcm16,
      sampleRate: tSampleRate,
      bitRate: 12800,
    );
  }

  ///停止录音
  /// [isCancel] 是否取消评测
  Future<void> stopRecord(
      {bool isCancel = false, bool sendResult = true}) async {
    debugPrint('debugPrint---> 调用了停止录音 ');
    await recorder.stopRecorder();

    if (sendResult) {
      /// 抛出停止录音回调
      _listener?.call(YDEvalResultEntity(
        path: _mPath,
        result: "",
        type: YDEvalEventType.endRecord,
        originText: _originalText,
        evalType: evalType,
      ));
    }

    if (_mRecordingDataSubscription != null) {
      await _mRecordingDataSubscription!.cancel();
      _mRecordingDataSubscription = null;
      pcmToWav();
    }

    _isCancel = isCancel;

    // if(_dbPeakSubscription != null){
    //   _dbPeakSubscription!.cancel();
    //   _dbPeakSubscription = null;
    // }

    debugPrint('停止录音 路径: $_mPath');
    endEval();
    // channel?.sink.close();
    // channel = null;
  }

  /// 结束评测
  void endEval() {
    _socketChannel?.sink.add("{\"end\":\"true\"}");
    // channel?.sink.close();
  }

  /// pcm转wav
  void pcmToWav() async {
    try {
      if (!File(_mPath).existsSync()) {
        debugPrint('文件不存在');
        return;
      }
      var file = File(_wavPath);
      if (!file.existsSync()) {
        file.create(recursive: true);
      }

      /// 放大播放音量
      convertPcmToWav(
          File(_mPath).readAsBytesSync(), _wavPath, tSampleRate, 1, 2.5);
      // final wav = await Wav.readFile(_mPath);
      // for (final chan in wav.channels) {
      //   for (int i = 0; i < chan.length; ++i) {
      //     chan[i] *= 2; // Decrease the volume.
      //   }
      // }

      // await wav.writeFile(_wavPath);
      // FlutterSoundHelper()
      //     .pcmToWave(
      //         inputFile: _mPath, outputFile: _wavPath, sampleRate: tSampleRate)
      //     .then((value) {
      //   _isConvertWav = true;
      // });
    } catch (e) {
      _isConvertWav = true;
      debugPrint('pcm转wav失败 $e');
    }
  }

  Future<void> convertPcmToWav(Uint8List pcmData, String outputFilePath,
      int sampleRate, int channels, double volumeMultiplier) async {
    int byteRate = sampleRate * channels * 2; // 2 bytes per sample (16-bit PCM)
    int blockAlign = channels * 2; // 2 bytes per sample

    Uint8List amplifiedPcmData = amplifyPcm(pcmData, volumeMultiplier);

    // WAV header
    List<int> header = [
      // RIFF header
      0x52,
      0x49,
      0x46,
      0x46,
      // 'RIFF'
      ..._intToBytes(amplifiedPcmData.length + 36, 4),
      // File size - 8 bytes for 'RIFF' and size itself
      0x57,
      0x41,
      0x56,
      0x45,
      // 'WAVE'

      // fmt subchunk
      0x66,
      0x6d,
      0x74,
      0x20,
      // 'fmt '
      0x10,
      0x00,
      0x00,
      0x00,
      // Subchunk1 size (16 for PCM)
      0x01,
      0x00,
      // Audio format (1 for PCM)
      ..._intToBytes(channels, 2),
      // Number of channels
      ..._intToBytes(sampleRate, 4),
      // Sample rate
      ..._intToBytes(byteRate, 4),
      // Byte rate
      ..._intToBytes(blockAlign, 2),
      // Block align
      0x10,
      0x00,
      // Bits per sample (16 bits)

      // data subchunk
      0x64,
      0x61,
      0x74,
      0x61,
      // 'data'
      ..._intToBytes(amplifiedPcmData.length, 4),
      // Subchunk2 size (number of bytes of PCM data)
    ];

    // Combine header and PCM data
    Uint8List wavData = Uint8List.fromList(header + amplifiedPcmData);

    // Write the WAV file
    final outputFile = File(outputFilePath);
    await outputFile.writeAsBytes(wavData);
    _isConvertWav = true;
  }

  Uint8List amplifyPcm(Uint8List pcmData, double multiplier) {
    ByteData byteData = ByteData.sublistView(pcmData);
    for (int i = 0; i < pcmData.length; i += 2) {
      int sample = byteData.getInt16(i, Endian.little);
      sample = (sample * multiplier)
          .clamp(-32768, 32767)
          .toInt(); // Clamp to avoid overflow
      byteData.setInt16(i, sample, Endian.little);
    }
    return pcmData;
  }

  List<int> _intToBytes(int value, int length) {
    List<int> result = [];
    for (int i = 0; i < length; i++) {
      result.add(value & 0xFF);
      value = value >> 8;
    }
    return result;
  }

  /// 播放录音
  void playRecord({String url = '', Codec codec = Codec.pcm16}) {
    player.openPlayer().then((value) {
      player.setSubscriptionDuration(const Duration(milliseconds: 100));
      _playListener = player.onProgress?.listen((event) {
        debugPrint('播放进度 ${event.position} e -> ${event.duration}');
        _onPlayback?.call(event);
        if (event.position >= event.duration) {
          _playListener?.cancel();
        }
      });
      player.startPlayer(
          fromURI: url.isEmpty ? _wavPath : url,
          codec: codec,
          whenFinished: () {
            debugPrint('播放完成');
            _onPlayback?.call(PlaybackDisposition(
                position: const Duration(seconds: 1),
                duration: const Duration(seconds: 1)));
          });
    });
  }

  /// 设置播放回调
  void setOnPlayback(Function(PlaybackDisposition playback) onPlayback) {
    _onPlayback = onPlayback;
  }

  /// 停止播放
  Future<void> stopPlayer() async {
    await player.stopPlayer();
  }

  /// 释放
  void dispose() {
    stopRecord();
    recorder.closeRecorder();
    player.closePlayer();
    _socketChannel?.sink.close();
    _socketChannel = null;
    _playListener?.cancel();
    _playListener = null;
    _onPlayback = null;
  }
}
